package com.jms.boot.core;

import com.jms.boot.annotation.AopConfig;
import com.jms.boot.annotation.MvcConfig;
import com.jms.boot.config.builder.ConfigBuilder;
import com.jms.boot.context.JmsBootApplicationContext;
import com.jms.boot.context.JmsBootBeanContext;
import com.jms.boot.context.JmsBootJDBCContext;
import com.jms.boot.context.JmsBootListenerContext;
import com.jms.framework.aop.context.AopContext;
import com.jms.framework.config.JmsConfig;
import com.jms.framework.mvc.context.MvcContext;

import java.io.*;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;

/**
 * ApplicationBoot 类是 JMS Boot 应用程序的启动入口。
 * 它负责加载应用程序配置、初始化上下文，并调用启动适配器来启动服务器和初始化 MyBatis-Plus。
 *
 * @author jms
 * @since 2025-01-07
 */
public class ApplicationBoot {

    /**
     * 启动应用程序的主要方法。
     * 加载配置文件，初始化应用程序上下文，并启动服务器和 MyBatis-Plus。
     *
     * @param bootClass 引导类，通常带有 @ApplicationBoot 注解
     * @param args        命令行参数
     */
    public void Boot(Class<?> bootClass, String[] args) {
        // 打印启动 Banner
        printBanner();

        // 加载应用程序配置
        JmsConfig jmsConfig = ConfigBuilder.getInstance()
                .builder(bootClass, args)
                .copyPropertiesToAttributes()
                .getBaseComponentScanPaths()
                .build();

        // 构造应用程序上下文
        JmsBootApplicationContext applicationContext = JmsBootApplicationContext.getInstance();
        applicationContext.setJmsConfig(jmsConfig);

        // 构造aop上下文
        AopContext aopContext = null;
        if (bootClass.isAnnotationPresent(AopConfig.class)) {
            try {
                AopConfig aopConfig = bootClass.getAnnotation(AopConfig.class);
                Class<AopContext> aopContextClass = (Class<AopContext>) Class.forName(aopConfig.contextClassName());
                Method contextMethod = aopContextClass.getMethod(aopConfig.getContextMethodName());
                aopContext = (AopContext) contextMethod.invoke(null, new Object[0]);
                aopContext.setApplicationContext(applicationContext);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

        //构造bean上下文
        JmsBootBeanContext beanContext = JmsBootBeanContext.getInstance();
        beanContext.setApplicationContext(applicationContext);

        //构造JDBC上下文
        JmsBootJDBCContext jdbcContext = JmsBootJDBCContext.getInstance();
        jdbcContext.setApplicationContext(applicationContext);

        // 构造mvc上下文
        MvcContext mvcContext = null;
        if (bootClass.isAnnotationPresent(MvcConfig.class)) {
            try {
                MvcConfig mvcConfig = bootClass.getAnnotation(MvcConfig.class);
                Class<MvcContext> mvcContextClass = (Class<MvcContext>) Class.forName(mvcConfig.contextClassName());
                Method contextMethod = mvcContextClass.getMethod(mvcConfig.getContextMethodName());
                mvcContext = (MvcContext) contextMethod.invoke(null, new Object[0]);
                mvcContext.setApplicationContext(applicationContext);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

        //构造监听上下文
        JmsBootListenerContext listenerContext = JmsBootListenerContext.getInstance();
        listenerContext.setApplicationContext(applicationContext);

        applicationContext.setAopContext(aopContext);
        applicationContext.setBeanContext(beanContext);
        applicationContext.setJdbcContext(jdbcContext);
        applicationContext.setMvcContext(mvcContext);
        applicationContext.setListenerContext(listenerContext);

        applicationContext.init();  // 初始化上下文
        beanContext.afterInit(); // 添加 BeanContext 的 afterInit 方法

        //添加上下文销毁的钩子
        Runtime.getRuntime().addShutdownHook(new Thread(applicationContext::destroy, "JmsBootApplicationShutdown"));

        applicationContext.getListenerContext().getStartListenerChain().doChain();
    }

    /**
     * 打印应用程序启动时的 banner。
     * banner 文件位于 classpath 下的 "banner.txt" 文件中。
     */
    private void printBanner() {
        try {
            InputStream bannerStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("banner.txt");
            if (bannerStream != null) {
                try (BufferedReader bannerReader = new BufferedReader(new InputStreamReader(bannerStream, StandardCharsets.UTF_8))) {
                    String bannerTxt;
                    while ((bannerTxt = bannerReader.readLine()) != null) {
                        System.out.println(bannerTxt);
                    }
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

}
